package com.cooldatasoft.support.git;

import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepository;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

/**
 * User: brano
 */
public class JavaGitManager {
	private static final String SEPARATOR = System.getProperties().getProperty(
			"file.separator");
	private static final String REPO_DIR = ".." + SEPARATOR + "repositories";
	private static JavaGitManager manager;

	private List<RepositoryProject> repositories;

	static {
		manager = new JavaGitManager();
	}

	private JavaGitManager() {
		this.repositories = getExistingRepositories();
	}

	private List<RepositoryProject> getExistingRepositories() {
		List<RepositoryProject> projects = new ArrayList<RepositoryProject>();

		String projectName;
		long projectId;

		File repositoriesDir = new File(REPO_DIR);
		if (repositoriesDir.exists()) {
			File[] files = repositoriesDir.listFiles();
			for (File file : files) {
				StringTokenizer st = new StringTokenizer(file.getName(), "_");
				if (st.countTokens() != 2) {
					continue;
				}
				projectName = st.nextToken();
				projectId = Long.parseLong(st.nextToken());

				try {
					Repository repository = new FileRepository(getRepoGitPath(
							projectName, projectId));
					projects.add(new RepositoryProject(repository, projectId,
							projectName));
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return projects;
	}

	public boolean existProject(String projectName, long idProject) {
		return (getRepositoryProject(projectName, idProject) == null ? false
				: true);
	}

	public static JavaGitManager getInstance() {
		return manager;
	}

	public void deleteRepository(String projectName, long idProject) {
		RepositoryProject repositoryProject = getRepositoryProject(projectName,
				idProject);
		repositories.remove(repositoryProject);
		try {
			FileUtils.deleteDirectory(repositoryProject.getRepository()
					.getDirectory().getParentFile());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void createRepository(String projectName, long idProject) {
		RepositoryProject newRepoProject = new RepositoryProject(idProject,
				projectName);
		if (!repositories.contains(newRepoProject)) {
			try {
				File workingDir = new File(getRepoGitPath(projectName,
						idProject));
				Repository newRepository = new FileRepository(workingDir);
				newRepository.create();
				newRepository.close();
				newRepoProject.setRepository(newRepository);
				repositories.add(newRepoProject);
				System.out.println("Repository for project: " + projectName
						+ " was created");
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("Repository for project: " + projectName
					+ " already exists");
		}
	}

	private String getRepoGitPath(String projectName, long idProject) {
		return REPO_DIR + SEPARATOR + projectName + "_" + idProject + SEPARATOR
				+ ".git";
	}

	public RepositoryProject getRepositoryProject(String projectName,
			long idProject) {
		RepositoryProject tempRepo = new RepositoryProject(idProject,
				projectName);
		for (RepositoryProject repository : repositories) {
			if (repository.equals(tempRepo)) {
				return repository;
			}
		}

		return null;
	}

	public FileNodeInterface getRepositoryStructureForProject(
			String projectName, long idProject) {
		File parentFile = getRepositoryProject(projectName, idProject)
				.getRepository().getDirectory().getParentFile();
		return walkTrough(parentFile);
	}

	private FileNodeInterface walkTrough(File parentFile) {
		FileNodeInterface fileNode;

		if (!parentFile.getName().equals(".git") && parentFile.isDirectory()) {
			List<FileNodeInterface> children = new ArrayList<FileNodeInterface>();

			File[] list = parentFile.listFiles();
			if (list != null) {
				for (File file : list) {
					if (!file.getName().equals(".git")) {
						children.add(walkTrough(file));
					}
				}
			}

			fileNode = new DirectoryNode(parentFile.lastModified(), children,
					parentFile.getName());
		} else {
			fileNode = new FileNode(parentFile.lastModified(),
					parentFile.length(), parentFile.getName());
		}

		return fileNode;
	}

	public ArrayList<CommitInfoWrapper> getAllCommitsInfo(String projectName, long idProject) {
		ArrayList<CommitInfoWrapper> commitList = new ArrayList<CommitInfoWrapper>();
		
		RepositoryProject repProject = getRepositoryProject(projectName, idProject);
		
		if (repProject != null) {
			Repository repository = repProject.getRepository();
			Git git = new Git(repository);
			RevWalk walk = new RevWalk(repository);
			RevCommit commit = null;

			Iterable<RevCommit> logs;
			try {
				logs = git.log().call();
				Iterator<RevCommit> i = logs.iterator();

				while (i.hasNext()) {
					commit = walk.parseCommit(i.next());
					commitList.add(new CommitInfoWrapper(
							commit.getAuthorIdent().getEmailAddress(),
							commit.getAuthorIdent().getWhen(),
							commit.getAuthorIdent().getName()
							)
					);
				}
			} catch (NoHeadException e) {
				//e.printStackTrace();
				//it's OK 
				System.out.println("No initial commit detected");
			} catch (JGitInternalException e) {
				e.printStackTrace();
			} catch (MissingObjectException e) {
				e.printStackTrace();
			} catch (IncorrectObjectTypeException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		} else {
			System.out.println("Repository not found!");
		}
		
		return commitList;
	}
}
